#include <string>
#include <iostream>
#include <filesystem>
#include <fstream>
#include <cmath>
#include <stdexcept>
#include <iomanip>
#include <algorithm>

#include "rocksdb/db.h"
#include "rocksdb/options.h"

using namespace rocksdb;
using namespace std;
namespace fs = filesystem;

fs::path data_path = "./dataset/DataSet/stackExchange/";
fs::path database_path = "./rocksdb-stack-overflow";

struct HumanReadable
{
    uintmax_t size{};

private:
    friend ostream &operator<<(ostream &os, HumanReadable hr)
    {
        int i{};
        double mantissa = hr.size;
        for (; mantissa >= 1024.; mantissa /= 1024., ++i)
        {
        }
        mantissa = ceil(mantissa * 10.) / 10.;
        os << mantissa << "BKMGTPE"[i];
        return i == 0 ? os : os << "B (" << hr.size << ')';
    }
};

int main()
{
    DB *db;
    Options options;

    // Optimize RocksDB. This is the easiest way to get RocksDB to perform well
    options.IncreaseParallelism();
    options.OptimizeLevelStyleCompaction();
    // create the DB if it's not already present
    options.create_if_missing = true;

    // open DB
    Status s = DB::Open(options, database_path.string(), &db);
    assert(s.ok());

    cout << "please wait, this may takes a few minutes\n";

    struct HumanReadable tot_size;
    int done_records = 0;
    int dup_records = 0;

    // iterate data path for all files in data_path
    for (fs::recursive_directory_iterator file(data_path), end; file != end; ++file)
        if (!is_directory(file->path()))
        {
            string path = file->path().string();
            cout << "processing file " << path << '\n';

            ifstream inFile(path);
            string line;
            while (getline(inFile, line))
            {
                if (line.size() < 3)
                    continue;
                //record start with <row
                size_t found = line.find("<row");
                if (found == string::npos)
                {
                    // cout << line << '\n';
                    // cout << "doesn't find <row" << "\n\n";
                    continue;
                }

                string key;

                size_t start = line.rfind(" Id=");
                //<row  ...  Id="12345" ... ></row>
                //start:    ^
                if (start == string::npos)
                {
                    cerr << line << '\n';
                    cerr << "doesn't find Id="
                         << "\n\n";
                    return -1;
                }

                start += 5;
                //<row  ... Id="12345" ... ></row>
                //start:        ^

                size_t end = line.find('"', start);
                //<row  ... Id="12345" ... ></row>
                //end:               ^

                if (end == string::npos)
                {
                    cerr << line << '\n';
                    cerr << "doesn't find right quotation!"
                         << "\n\n";
                    return -1;
                }

                // key = 12345
                key = line.substr(start, end - start);

                s = db->Put(WriteOptions(), key, line);
                if (!s.ok())
                    cerr << s.ToString() << endl;
                else
                {
                    ++done_records;
                    tot_size.size += line.size() + key.size();
                }
                
            }
        }
    cout << done_records << " records have been insert into rocks databse! " << endl;
    cout << "The inserted size of keys and values are " << tot_size << endl;
    cout << "rocks database is located in: " << database_path.string() << endl;

    delete db;
}